/**
 * @作者 zw
 * @微信公号 搬砖暗夜码农
 * @开源项目 $ https://gitee.com/zhouwei5208888/spring-boot-distributed
 * @我的博客 $ http://itzhouwei.com
 * @创建时间 2023/6/3 10:21 上午
 */
package com.itzhouwei.netty.day2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 *
 *
 *  netty  服务端连接的建立流程 面试题
 *
 * Netty是一个基于Java的高性能网络编程框架，它提供了异步、事件驱动的网络应用程序开发模型。在Netty中，服务端连接的建立流程通常包括以下几个步骤：
 *
 * 1.创建ServerBootstrap对象：ServerBootstrap是Netty中用于创建和配置服务端的引导类。通过创建ServerBootstrap对象，我们可以设置服务端的各种参数和属性。
 *
 * 2.设置EventLoopGroup：Netty使用EventLoopGroup来处理网络事件，包括接收客户端连接、读写数据等。通常情况下，我们需要创建两个EventLoopGroup，一个用于处理客户端的连接事件（即接收客户端连接），另一个用于处理已经连接的客户端的I/O事件（即读写数据）。
 *
 * Boss Group：用于接收客户端连接的EventLoopGroup。
 * Worker Group：用于处理已连接客户端的I/O事件的EventLoopGroup。
 * 一般情况下，我们可以使用NioEventLoopGroup来创建这两个EventLoopGroup。
 *
 * 3.设置Channel类型和ChannelHandler：在Netty中，Channel是网络连接的抽象，它代表了一个打开的连接。通过ServerBootstrap的channel()方法，我们可以设置服务端的Channel类型，例如NioServerSocketChannel。
 *
 * 接下来，我们需要为ServerBootstrap设置ChannelHandler，ChannelHandler负责处理网络事件和业务逻辑。常见的ChannelHandler包括解码器、编码器、业务处理器等。
 *
 * 4.绑定端口并启动服务端：通过调用ServerBootstrap的bind()方法，将服务端绑定到指定的IP地址和端口号上。绑定完成后，服务端会开始监听指定端口上的客户端连接。
 *
 * 例如：ChannelFuture future = bootstrap.bind(port).sync();
 *
 * 在这里，bind()方法返回一个ChannelFuture对象，它表示异步操作的结果。通过调用ChannelFuture的sync()方法，可以等待绑定操作完成。
 *
 * 5.关闭服务端：当需要关闭服务端时，我们可以调用ServerBootstrap的close()方法。类似地，close()方法也返回一个ChannelFuture对象，可以使用sync()方法等待关闭操作完成。
 *
 * 例如：future.channel().closeFuture().sync();
 *
 * 这里，通过调用closeFuture()方法获取到服务端的关闭事件的ChannelFuture对象，并使用sync()方法等待关闭操作完成。
 *
 *
 * @description: Netty 服务端代码
 * @author：zw，微信公共号：yingshengzw
 * @date: 2021/8/28
 * @Copyright：公众号：搬砖暗夜码农 | 博客：https://itzhouwei.com - 沉淀、学习、分享、成长，让自己和他人都能有所收获！
 */
@SuppressWarnings("all")
public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        // 第一步，分别创建两个处理网络的EventLoopGroup。
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            // 第二步，初始化服务器
            ServerBootstrap sb = new ServerBootstrap();//相当于Netty服务器
            // 第三步，给服务器做一系列的配置
            sb.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {  //处理每个连接的 SocketChannel,SocketChannel代表每一个连接

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //处理业务逻辑
                            socketChannel.pipeline().addLast(new NettyServerHandler()); //针对网络请求的处理逻辑
                        }
                    });

            System.out.println("Server 启动了");
            //第四步 绑定端口
            ChannelFuture channelFuture = sb.bind(50988).sync(); //监听指定端口
            // 第五步，等待服务器关闭
            channelFuture.channel().closeFuture().sync();//同步等待关闭启动服务器的结果
        }finally {
            parentGroup.shutdownGracefully();
            childGroup.shutdownGracefully();
        }
		 /***
         *
         *  框架 里面 bind () 里面具体代码
         *   创建和初始化一个 ServerSocketChannel 并注册到 Selector  轮询复用组件上去
         *   final ChannelFuture initAndRegister() {
         *         Channel channel = null;
         *         try {
         *             channelFactory 通过工厂类获取ServerSocketChannel
         *             channel = channelFactory.newChannel();
         *             初始化 ServerSocketChannel
         *             init(channel);
         *         } catch (Throwable t) {
         *             if (channel != null) {
         *                 // channel can be null if newChannel crashed (eg SocketException("too many open files"))
         *                 channel.unsafe().closeForcibly();
         *                 // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
         *                 return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
         *             }
         *             // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
         *             return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
         *         }
         *        拿出来之前创建的 EventLoopGroup 然后把Channel 注册到EventLoopGroup 上 目的是轮询各种 channel  上的网络事件
         *        我们猜测是不是 让 EvetnLoopGroup 中的独立线程利用一个 Selector 来注册 Channel，并轮询网络事件。
         *
         *         ChannelFuture regFuture = config().group().register(channel);
         *         if (regFuture.cause() != null) {
         *             if (channel.isRegistered()) {
         *                 channel.close();
         *             } else {
         *                 channel.unsafe().closeForcibly();
         *             }
         *         }
         *
         *
         *
         *         return regFuture;
         *     }
         */
    }
}
